package force;

import assembly.Mass;
import java.awt.Dimension;
import java.util.Scanner;
import simulation.SpringiesConstants;
import simulation.Model;
import util.Location;
import util.Vector;


/**
 * Represents the force of a single wall, inversely proportional to distance
 * 
 * @author Scott Valentine
 * @author Dagbedji Fagnisse
 * 
 */
public class WallRepulsion extends Force {
    /**
     * default dimension for the walls
     */
    private static final Dimension DEFAULT_WALL_DIMENSION = new Dimension(800, 600);

    /**
     * angle direction for down
     */
    private static final int DOWN = 90;

    /**
     * angle direction for left
     */
    private static final int LEFT = 180;

    /**
     * angle direction for right
     */
    private static final int UP = 270;

    /**
     * angle direction for right
     */
    private static final int RIGHT = 0;

    private static final String KEYWORD = SpringiesConstants.WALL_KEYWORD;

    private static final int[] TOGGLE_KEYS = SpringiesConstants.WALL_TOGGLES;

    private int myID;
    private double myMagnitude;
    private double myExponent;
    private Dimension myBounds;
    private double myDirection;

    /**
     * represents the force from a single wall
     * 
     * @param id number of the wall: 1 is top; 2 is right; etc.
     * @param magnitude of the force
     * @param exponent by which the force is inversely proportional to distance away from given wall
     * @param bounds indicate the location of the walls
     */
    public WallRepulsion (int id, double magnitude, double exponent, Dimension bounds) {
        myID = id;
        myBounds = bounds;
        myMagnitude = magnitude;
        myExponent = exponent;
        myDirection = DOWN;
        toggle();

    }

    /**
     * creates a default wall force based on which of the four walls
     * 
     * @param id of the wall for force
     */
    public WallRepulsion (int id) {
        this(id, 1, 1, DEFAULT_WALL_DIMENSION);
    }

    /**
     * Default constructor. Builds a sample WallRepulsion force.
     */
    public WallRepulsion () {
        // TODO this or getSample()
        this(1, 1, 1, DEFAULT_WALL_DIMENSION);
        // don't forget to untoggle force
        toggle();
    }

    /**
     * creates a direction for the force to act
     * 
     * @param m
     *        the mass on which the force is to be exerted.
     */
    public Vector getForceOn (Mass m) {
        if (!isForceOn()) { return new Vector(); }
        Location currentPosition = m.getCenter();

        double x = currentPosition.getX();
        double y = currentPosition.getY();

        double dist = getDistanceAndSetDirection(x, y);
        Vector vWall = getWallVector(dist);
        return vWall;
    }

    /**
     * scales and creates new vector that represents the force of the passed
     * wall. This vector's magnitude is inversely proportional to the passed distance.
     * 
     * @param wall
     *        - wall
     * @param dist
     *        - current mass' distance from wall
     * @return
     */
    private Vector getWallVector (double dist) {
        double exp = Math.pow(dist, myExponent);
        double factor = myMagnitude / exp;
        Vector res = new Vector(myDirection, factor);
        return res;
    }

    /**
     * returns distance away from wall, given by id
     * AND sets the direction in which to force acts, based on the wall id
     * 
     * there is probably a way to make this easier to generalize for any kind of
     * wall but not sure how to go about it.
     * 
     * looking for way to combine the if statements from here and above into one
     * 
     * @param id
     *        - which wall
     * @param x
     *        -current location of mass
     * @param y
     *        - current location of mass
     * @param bounds
     *        - where the walls are located
     * @return
     */
    private double getDistanceAndSetDirection (double x, double y) {
        double dist = 0;
        // if & else check for which wall it is. Once again, required that id
        // follows specific rules
        // north and south walls
        if (myID % 2 == 1) {
            // from north wall
            if (myID == 1) {
                dist = y;
                myDirection = DOWN;
            }
            // from south wall
            else {
                dist = myBounds.height - y;
                myDirection = UP;
            }
        }
        // east and west walls
        else {
            // from east wall
            if (myID == 2) {
                dist = myBounds.width - x;
                myDirection = LEFT;
            }
            // from west wall
            else {
                dist = x;
                myDirection = RIGHT;
            }
        }
        return dist;
    }

    @Override
    public Force load (Scanner line, Model model) {
        int id = line.nextInt();
        double magnitude = line.nextDouble();
        double exponent = line.nextDouble();
        return new WallRepulsion(id, magnitude, exponent, model.getBounds());
    }

    @Override
    public String getKeyword () {
        return KEYWORD;
    }

    @Override
    public int getToggleKey () {
        return TOGGLE_KEYS[myID - 1];
    }

}
